//! # Playlist - 播放列表模块
//!
//! 主要用于管理播放列表。

use std::{path::PathBuf, time::Duration};

use crate::{
    config::mapper::MapperConfig,
    event::{MP4GameEvent, TrackFinishEvent},
    state::AppState,
};
use tokio::sync::oneshot;

/// 提供映射器和路径，打开 Midi 文件
///
/// # 参数
///
/// * `mapper` - 映射器配置
/// * `path` - Midi 文件路径
///
/// # 返回值
///
/// * [`Ok`] - 返回播放器 ID
/// * [`Err`] - 返回错误信息
#[tauri::command]
pub async fn open(
    mapper: MapperConfig,
    path: PathBuf,
    state: tauri::State<'_, AppState<'_>>,
) -> Result<String, String> {
    state.new_player(mapper, path).await
}

/// 获取 Midi 持续时间（单位：毫秒）
///
/// # 参数
///
/// * `id` - 播放器 ID
///
/// # 返回值
///
/// * [`Ok`] - 返回 Midi 持续时间
/// * [`Err`] - 返回错误信息
#[tauri::command]
pub async fn duration(id: String, state: tauri::State<'_, AppState<'_>>) -> Result<u32, String> {
    // 记录日志
    log::info!("获取 ID = {id} 的持续时间");

    // 解析 ID
    let id = state.parse_and_check_id(&id).await?;

    // 获取持续时间
    Ok(state
        .players
        .read()
        .await
        .get(&id)
        .unwrap()
        .duration()
        .await
        .as_millis() as u32)
}

/// 播放 Midi 文件
///
/// # 参数
///
/// * `id` - 播放器 ID
/// * `start_point` - 开始播放的位置
#[tauri::command]
pub async fn play(
    id: String,
    start_point: Option<u64>,
    state: tauri::State<'_, AppState<'_>>,
    window: tauri::Window,
) -> Result<(), String> {
    // 记录日志
    log::info!("将 {id} 在 {start_point:?} 处开始播放");

    // 解析 ID
    let id = state.parse_and_check_id(&id).await?;

    // 获取播放器
    if let Some(player) = state.players.write().await.get_mut(&id) {
        // 创建停止信号
        let (sender, receiver) = oneshot::channel();

        // 播放
        player
            .play(start_point.map(|e| Duration::from_millis(e)), sender)
            .await;

        // 监听停止信号
        tokio::spawn(async move {
            // 获取停止信号
            let is_pause = receiver.await.unwrap_or(false);

            // 记录日志
            log::info!("对 {id} 的播放 {}", if is_pause { "暂停" } else { "结束" });

            // 向前端发送停止信号
            MP4GameEvent::PlayFinish(TrackFinishEvent {
                id: id.to_string(),
                is_pause,
            })
            .emit_all(&window)
            .unwrap();
        });
    }

    Ok(())
}

/// 转换 Midi 文件
///
/// # 参数
///
/// * `id` - 播放器 ID
/// * `start_point` - 开始播放的位置
#[tauri::command]
pub async fn convert(
    id: String,
    start_point: Option<u64>,
    state: tauri::State<'_, AppState<'_>>,
    window: tauri::Window,
) -> Result<(), String> {
    // 记录日志
    log::info!("将 {id} 在 {start_point:?} 处开始转换");

    // 解析 ID
    let id = state.parse_and_check_id(&id).await?;

    // 获取播放器
    if let Some(player) = state.players.write().await.get_mut(&id) {
        // 创建停止信号
        let (sender, receiver) = oneshot::channel();

        // 转换
        player
            .convert(start_point.map(|e| Duration::from_millis(e)), sender)
            .await;

        // 监听停止信号
        tokio::spawn(async move {
            // 获取停止信号
            let is_pause = receiver.await.unwrap_or(false);

            // 记录日志
            log::info!("对 {id} 的转换 {}", if is_pause { "暂停" } else { "结束" });

            // 向前端发送停止信号
            MP4GameEvent::ConvertFinish(TrackFinishEvent {
                id: id.to_string(),
                is_pause,
            })
            .emit_all(&window)
            .unwrap();
        });
    }

    Ok(())
}

/// 停止播放 Midi
///
/// # 参数
///
/// * `id` - 播放器 ID
#[tauri::command]
pub async fn stop(id: String, state: tauri::State<'_, AppState<'_>>) -> Result<(), String> {
    // 记录日志
    log::info!("停止 {id}");

    // 解析 ID
    let id = state.parse_and_check_id(&id).await?;

    // 获取播放器并停止
    if let Some(player) = state.players.write().await.get_mut(&id) {
        player.stop().await;
    }

    Ok(())
}

/// 关闭 Midi 文件及其对应的播放器
///
/// # 参数
///
/// * `id` - 播放器 ID
#[tauri::command]
pub async fn close(id: String, state: tauri::State<'_, AppState<'_>>) -> Result<(), String> {
    // 记录日志
    log::info!("关闭 {id}");

    // 解析 ID
    let id = state.parse_and_check_id(&id).await?;

    // 获取播放器并停止
    if let Some(player) = state.players.write().await.get_mut(&id) {
        if player.on_track().await {
            player.stop().await;
        }
    }

    // 移除播放器
    let _ = state.players.write().await.remove(&id);

    Ok(())
}
